package es_deusto_engineering_IS_HanoiTower;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;

import es.deusto.ingenieria.is.search.algorithms.Node;
import es.deusto.ingenieria.is.search.algorithms.SearchMethod;
import es.deusto.ingenieria.is.search.formulation.Problem;
import es.deusto.ingenieria.is.search.formulation.State;

public class HanoiTowerProblem extends Problem {

	int numDisks;
	int numPegs;
	
	/**Main constructor of the class
	 * @param numDisks
	 */
	public HanoiTowerProblem(int numDisks, int numPegs)
	{
		this.numDisks = numDisks;
		this.numPegs = numPegs;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}
	
	//We receive a given state and check if it is the final state
	public boolean isFinalState(State state) {
		//we check if the class of the state is Environment
		if(state.getClass().getName().equals("es_deusto_engineering_IS_HanoiTower.Environment"))
		{
			//if the destination Peg has the entire stack of disks, it is the final state
			if(((Environment)state).getDestination().getDisks().size() == numDisks)
			{
				int size_p = ((Environment)state).getPegs().size();
				int cont = 0;
				//We check that the rest of the pegs are empty
				for(int i = 0; i < size_p; i++)
				{
					if(i != ((Environment)state).getDestination().getPosition())
					{
						if(!((Environment)state).getPegs().get(i).getDisks().isEmpty())
						{
							cont++;
						}
					}
				}
				if(cont > 0)
				{
					return false;
				}
				else
					return true;
			}
			//when the destination peg hasn't got the entire stack of disks yet.
			return false;
		}
		return false;
	}
	
	//We create different instances of Action and we add them to the HanoiTowerProblem
	public void createOperators()
	{
		//Action: subclass of Operation that has as attribute a String object from which we can split the
		//origin and destination pegs of the movement.
		
		//We create all the operators that can be possible
		for(int i = 0; i < numPegs; i++)
		{
			for(int j=0; j < numPegs; j++)
			{
				Action o = new Action("From "+i+" to "+j);
				this.addOperator(o);
			}
			
		}
		
	}
	
	//To encapsulate the different methods we need of SearchMethod class.
	public void solve(SearchMethod type)
	{
		//to print the initial time where the algorithm starts searching
		SimpleDateFormat formatter = new SimpleDateFormat("hh:mm:ss.SSS");
		System.out.println("* Start '" + type.getClass().getSimpleName() + "' (" + formatter.format(GregorianCalendar.getInstance().getTime()) + ")");
	    
		//we obtain the final node by searching, passing the problem and the initial state
		Node finalNode = type.search(this, this.getInitialStates().get(0));
	    System.out.println("* End '" + type.getClass().getSimpleName() + "' (" + formatter.format(GregorianCalendar.getInstance().getTimeInMillis()) + ")");
	      
	     System.out.println(finalNode);
	     System.out.println("Number of movements:" + finalNode.getDepth());
	     
	     //Create an empty list of operators
	     List<String> listOperators = new ArrayList<String>();
	     
	     //We need to know the path in order to get the goal
	     type.solutionPath(finalNode, listOperators);
	     System.out.println(listOperators.toString()+"\n");
	     
	     //Create a log with the operators needed to reach the goal. EXPLANATION INSIDE DIFFERENT LOGS.
	   //  type.createSolutionLog(listOperators);
	      
	}


}
